Streamlit in Snowflake で簡単なアンケートをつくってみた #SnowflakeDB

Streamlit in Snowflake で簡単なアンケートをつくってみた #SnowflakeDB

Clock Icon2024.01.28

はじめに

昨年12月に Streamlit in Snowflake が一般提供になりました。これにより、Snowflake の GUI である Snowsight 上で Streamlit によるアプリケーションの開発と実行ができるようになります。
この機能について、基本的なことをまとめておきたく、記事としました。

Streamlit in SnowflakeがGAになりました!数分でスタートできるので、是非試してみてください。https://t.co/1BnbuR285u

— Shota Asano@Snowflake japan (@ShotaAsanoSnow1) December 4, 2023

作成するアプリ

ここでは、簡単なアンケートを Streamlit で作成します。
ユーザーがアンケートに入力し、結果を送信すると、Snowflake 上のテーブルに入力結果が追加されるようにします。
アンケートを作成するにあたっての本記事での前提条件は以下とします。

  • アンケートの設定
    • 社内向けのアンケート
    • アンケート回答者は Snowflake アカウントを所有している(アンケートの回答時は Snowflake にログインする)
  • 結果の格納先となる、データベース・スキーマ・テーブルは作成済み
    • Streamlit アプリでは既存のテーブルにレコードを追加する
  • Streamlit アプリとアンケートデータの格納先テーブルは同じスキーマに作成する

本記事で作成・使用する代表的なオブジェクトの名称は以下になります。

  • データベース:streamlit_db
  • スキーマ:form
  • テーブル:EmployeeSatisfactionSurvey
  • ロール
    • streamlit_creator
      • Streamlit アプリを作成する(所有者)ロール
    • streamlit_user
      • Streamlit アプリの共有先ロール

手順

データベース・テーブルの作成

はじめに以下のオブジェクトを次のコマンドで作成します。

  • Streamlit アプリ、アンケートデータの格納先データベース・スキーマ
  • Streamlit アプリで使用するウェアハウス
--データベース・スキーマの作成
USE ROLE SYSADMIN;
CREATE DATABASE streamlit_db;
CREATE SCHEMA streamlit_db.form;

--ウェアハウスの作成
CREATE WAREHOUSE st_form_wh 
    WAREHOUSE_SIZE = xsmall 
    WAREHOUSE_TYPE = standard
    AUTO_SUSPEND = 60
    AUTO_RESUME = True
    INITIALLY_SUSPENDED = True;

次に、以下のコマンドでアンケートデータの格納先テーブルを作成します。
項目の内、回答日時はレコードが追加された日時が追加されるようにしました。その他の項目はアンケートでユーザーから入力される値が記録されるようにします。

--テーブルの作成
USE SCHEMA streamlit_db.form;
CREATE OR REPLACE TABLE EmployeeSatisfactionSurvey(
    ResponseDateTime TIMESTAMP DEFAULT CURRENT_TIMESTAMP,  -- 回答日時
    Department VARCHAR(255),-- 所属部署
    Age NUMBER,-- 年齢
    ExperienceCount VARCHAR(255),-- 経験社数
    Satisfaction NUMBER-- 満足度
);

Streamlit アプリ作成用ロールの作成

streamlit_creatorの名称で、Streamlit アプリの作成に使用するロールを作成します。

--roleの作成
USE ROLE USERADMIN;
CREATE ROLE streamlit_creator;

このロールに必要な各種権限を付与します。
以下の「streamlit権限」の4行のコマンドが Streamlit アプリの作成に必要な権限になります。
最後のコマンドでは、アンケートデータ格納先テーブルへの書き込み権限を付与しています。アプリケーションは、streamlit_creatorロールで実行されるので、このロールにテーブルへの書き込み権限を与えます。
前提条件にあるように、Streamlit アプリとアンケートデータ格納先テーブルは同じスキーマにあるので、ここでは INSERT 権限のみ付与しています。

About Streamlit in Snowflake | Snowflake Documentation

--権限の付与
USE ROLE SECURITYADMIN;
----ロール階層
GRANT ROLE streamlit_creator TO ROLE SYSADMIN;

----ウェアハウス
GRANT USAGE ON WAREHOUSE st_form_wh TO ROLE streamlit_creator;

----streamlit権限
GRANT USAGE ON SCHEMA streamlit_db.form TO ROLE streamlit_creator;
GRANT USAGE ON DATABASE streamlit_db TO ROLE streamlit_creator;
GRANT CREATE STREAMLIT ON SCHEMA streamlit_db.form TO ROLE streamlit_creator;
GRANT CREATE STAGE ON SCHEMA streamlit_db.form TO ROLE streamlit_creator;

--結果の格納先テーブルへの書き込み権限
--※アプリケーションと同じスキーマに作成しているので、usage は付与済み
GRANT INSERT ON TABLE streamlit_db.form.employeesatisfactionsurvey TO ROLE streamlit_creator;

Streamlit in Snowflake の使用

アプリケーションの追加

上記の手順で作成したstreamlit_creatorロールにスイッチしていることを確認し、メニューの「Streamlit」から「+ Streamlit App」を選択します。

作成するアプリケーションに関して、以下を指定し「Create」をクリックします。

  • アプリケーションの名称
  • アプリケーション実行に使用するウェアハウス
  • アプリケーションの作成先
    • データベース
    • スキーマ

UI の作成

サンプルコードが記載された編集画面が開くので、ここでは以下のコードに変更しました。 ポイントは以下になります。

  • get_active_session()でセッションを作成
  • st.form()でアンケートを作成
    • フォームを使用しないとアプリの起動時や入力値を更新すると都度実行され、テーブルに書き込まれてしまうので、ここではフォームを使用しています
import streamlit as st
from snowflake.snowpark.context import get_active_session

# タイトルと概要
st.title("従業員満足度アンケート")
st.write(
    "会社満足度に関するアンケートです。"
)

# セッション情報
session = get_active_session()

st.title('回答フォーム')

# フォームを作成
with st.form(key='survey_form'):

    # 所属部署
    selected_department = st.selectbox(
        "所属部署を選択してください",
         ["","営業部", "人事部", "経理部", "開発部", "マーケティング部"],
        index=0)

    # 年齢の入力
    age = st.number_input("年齢",value=30, min_value=0, max_value=100, step=1)

    # セレクトボックスで経験社数を尋ねる
    experience_count = st.selectbox(
        "これまでの経験社数を教えてください",
        ["","1社", "2社", "3社以上"],index=0)    

    # 満足度
    satisfaction = st.radio(
    "現在の仕事に対する満足度を評価してください(1が最低、5が最高):",
    [1, 2, 3, 4, 5])    

    # 送信ボタン
    submit_button = st.form_submit_button("送信")

# サブミット後の処理
if submit_button:
    # 全てのフィールドが記述されているかチェック
    if selected_department and experience_count :
        # 全ての回答を表示
        st.write("### 回答内容")
        st.write("所属部署:", selected_department)
        st.write("年齢:", age)
        st.write("経験社数:", experience_count)
        st.write("満足度:", satisfaction)
        session.sql(f"""INSERT INTO EmployeeSatisfactionSurvey (Department, Age, ExperienceCount, Satisfaction)
        VALUES ('{selected_department}', {age}, '{experience_count}', {satisfaction})""").collect()
    else:
        # 必要なフィールドが記述されていない場合はエラーメッセージを表示
       st.error("所属部署、経験社数を指定してください。")

コードを記述しつつ、画面右上の「Run」をクリックすると作成途中でも内容を確認できます。上記のコードを実行すると、下図のような UI が表示されます。

各項目を入力し「送信」をクリックすることで、指定のテーブルにアンケート結果が書き込まれる仕様です。
コマンドでは以下の箇所がこの処理に該当します。

session.sql(f"""INSERT INTO EmployeeSatisfactionSurvey (Department, Age, ExperienceCount, Satisfaction)
        VALUES ('{selected_department}', {age}, '{experience_count}', {satisfaction})""").collect()

ここでは、上記の手順でアプリケーションの実行に使用するstreamlit_creatorロールに対象テーブルへの INSERT 権限を付与済みですが、ここで権限がない場合、下図のようなエラーとなります。

アプリケーションの実行

下図のように各項目を入力し「送信」をクリックします。

ここではコードにおける「サブミット後の処理」として、ユーザーが「送信」ボタンをクリックすると、回答内容を表示するような設定としているので、下図が追加表示されます。

その後、結果格納先のテーブルを確認します。

USE ROLE SYSADMIN;
SELECT * FROM EMPLOYEESATISFACTIONSURVEY;

回答内容がレコードとして追加されていることが確認できました。

他のロールにアプリケーションを共有する

次に、他のロールにアプリケーションを共有します。以下のコマンドで、共有先ロール(streamlit_user)と共有先のユーザー(test_user)を作成しました。

--閲覧者用のロール
USE ROLE USERADMIN;
CREATE ROLE streamlit_user;

--user 追加
CREATE OR REPLACE USER test_user
    PASSWORD='パスワード'
    DEFAULT_ROLE = streamlit_user;

USE ROLE SECURITYADMIN;
GRANT ROLE streamlit_user TO ROLE SYSADMIN;
GRANT ROLE streamlit_user TO USER test_user;

test_user でログインします。この時点では、ロールに権限を与えていないので、アプリは確認できません。

アプリケーション作成者ロール(streamlit_creator)で再度アプリケーションを開き、画面右上の「Share」をクリックします。

ここで共有先のロールを指定できます。しかし、この時点で下図のように共有先としたいstreamlit_userは選択できません。

ドキュメントにもあるように、アプリケーションを共有するには、共有先のロールに、アプリケーション格納先であるデータベース・スキーマの USAGE 権限が必要です。

About Streamlit in Snowflake | Snowflake Documentation

以下のコマンドでデータベース・スキーマに対する USAGE 権限を付与します。

USE ROLE SECURITYADMIN;
GRANT USAGE ON SCHEMA streamlit_db.form TO ROLE streamlit_user;
GRANT USAGE ON DATABASE streamlit_db TO ROLE streamlit_user;

この状態で再度共有先を指定すると、下図のようにstreamlit_userが選択できるようになっています。

また、共有する際は、ロールに対して以下のオプションを選択できるようです。

  • View only
    • アプリケーションの閲覧のみ可能
  • View and share
    • アプリケーションの閲覧と、他ロールに対する閲覧専用アクセスの付与を可能とする

ここでは「View only」として [Done] をクリックします。
この画面でアプリケーションへのリンクも表示されるので、共有先のユーザーにこのリンクを共有するだけで簡単に共有ができそうです。
その後、このロールを付与したユーザーで画面を更新すると、共有先でもアプリケーションが表示されるようになります。

対象のロールの権限を確認すると下図のようになっていました。

SHOW GRANTS TO ROLE streamlit_user;

3行目のレコードが、Streamlit アプリの使用権限です。Streamlit アプリを作成すると、対象のスキーマにステージが作成され、そのステージ(Streamlit アプリ)への USAGE 権限が付与されています。

ここでは GUI で Streamlit アプリの使用権限付与を行いましたが、コマンドで実行する場合は以下になります。

--Streamlitの使用権限付与
GRANT USAGE ON STREAMLIT <ステージ名> TO ROLE <ロール>;
--Streamlitの使用権限の削除
REVOKE USAGE ON STREAMLIT <ステージ名> FROM ROLE <ロール>;

共有先でアプリケーションを実行

アプリケーションを開くと、画面右上に「View only」と表示されており、「Share」は選択できないようになっていました。

上図のように入力し「送信」をクリックします。

その後、SELECT 権限を持つユーザーでテーブルを確認します。

USE ROLE SYSADMIN;
SELECT * FROM EMPLOYEESATISFACTIONSURVEY;

入力内容が追加されていることを確認できました。

検証時に詰まった点

アプリケーション作成時に少し詰まった点を記載します。

CURRENT_USER 関数の返り値

執筆時点では、Streamlit アプリで Current_User 関数を実行しても値が返ってきません(None になる)。
このアンケートアプリでも、当初は回答時にユーザー名を自動的に記録されるようにしたかったのですが、上記の仕様によりここでは断念しました。
こちらは、コミュニティでも同様の投稿がある内容でした。

Current_user() returns None in Streamlit in Snowflake | Snowflake Community
How to get the current user name in the snowpark | Snowflake Community

また、注意点として Current_Role 実行時もアプリケーションの所有者ロールが返ってきます。

サポートされていない Streamlit 機能がある

現時点では一部、利用できない Streamlit 機能があります。

サポートされていないStreamlit機能 | Snowflake DOCUMENTATION

さいごに

Streamlit in Snowflake で簡単なアプリケーションを作成してみました。
従来の Snowflake のアクセス権管理の考え方で、Snowflake 上でシンプルなアプリケーションを容易に作成できるので、非常に強力な機能だなと触ってみて改めて感じました。
このアプリケーションの場合、ユーザーが何度も回答を入力できてしまうのですが、今後 Hybrid テーブルの一意制約も使用できるようになれば、この辺りも容易に回避策が練れそうです。
今回は以上になります。こちらの記事が何かの参考になれば幸いです。

Share this article

facebook logohatena logotwitter logo

© Classmethod, Inc. All rights reserved.